package geneticalgorithm_masterworker;


import java.io.BufferedReader;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import core_masterworker.Master;

import data.DataTest;

import mpi.MPI;
import mpi.Status;
import utils.*;

public class GAMaster extends Master {
	
	/* Tag information between master - worker */
	public MWUtils mwutils = new MWUtils();
	final int work_tag = mwutils.WORK_TAG;
	final int data_tag = mwutils.DATA_TAG;
	final int stop_tag = mwutils.STOP_TAG;

	
	public double[] prepareWork(int n, int nmax, String filename) {
		
		double[] a = new double[nmax];
		
		System.out.println("\n\t Read the number of components");
		  try{
			  FileReader inputstream = new FileReader(filename);
			  BufferedReader data = new BufferedReader(inputstream);
			  String ncomponents = data.readLine();
			  n	= Integer.parseInt(ncomponents);
			  
			  //System.out.println("\n\t Number of components n = " +n[0]);
			  for(int i = 0; i < n; i++){
				  String value = data.readLine();
				  a[i] = Double.parseDouble(value);
				  //System.out.println("\n\t Components a[" + i + "] =" + a[i]);
			  }
			  //System.out.println("\n");
		  }
		  catch(FileNotFoundException e){
			  System.out.println("\n\t File not found");
		  }
		  catch(EOFException endf){
			  System.out.println("\n\t reached end of file");
		  }
		  catch(IOException e){
			  System.out.println("\n\t File reading error");
		  }
		  return a;
	}
	
	/* Do parallel work */
	public void doParallel(int masterID, int nworkers, int components, int numberofgeneration, String filename){
    	
    	/* Set up master */
    	Status status;
		int id;
		int task_counter;
		int cid = 0;
		
		/* Set up for dynamic assignment */
		DataTest[] count = new DataTest[1];
		count[0] = new DataTest(cid);		
		task_counter = 0;
		
		System.out.println("Start count = " + cid);
		
		int maxiter = numberofgeneration;
		
		/* Initiate array to store results from worker */
		DataTest[] result = new DataTest[components]; /* with 1 extra place at 0 index of array to store the number of generation from worker */
		
		/* Initiate array to store values then to find the best value */ 
		double[][] store = new double[maxiter][4];
		
		/*
		 * In the number of cycles that input from maxiter
		 * equal to number of generation needed to be generated by workers
		 * Master send the count to worker
		 * that tell worker to generate a new generation
		 * that will be used to fetch values from list of 
		 * cross section areas
		 */
		
		/* Send each worker first count to work */
		for (int i = 0; i < nworkers; i++){
			MPI.COMM_WORLD.Send(count, 0, count.length, MPI.OBJECT, i+1, work_tag);
			cid++;
			count[0].updateData(cid);
			System.out.println("Current row = " + cid);
			task_counter++;
		}
		
		
		int i = 0; /* Use to count the generation send from workers */
		
		/* Receive results from workers and process */
		while(task_counter > 0){
			/* Receive a row from a worker */
			try{
				status = MPI.COMM_WORLD.Recv(result, 0, result.length, MPI.OBJECT, MPI.ANY_SOURCE, data_tag);
			}
			catch (NullPointerException e) {
				System.out.println("Result array is null pointer! Init before use");
				return;
			}
			--task_counter;
			id = status.source;
			
			/* More work */
			if(cid < maxiter){
				
				/* If so give this worker another row */
				MPI.COMM_WORLD.Send(count, 0, count.length, MPI.OBJECT, id, work_tag);
				cid++;
				count[0].updateData(cid);
				++task_counter;
			}
			
			else{
				/* Shut down worker */
				MPI.COMM_WORLD.Send(count, 0, 0, MPI.OBJECT, id, stop_tag);
			}			

			/* Pass result to x temporary array */
			int the_count = result[0].getDataInt();
			
			i = the_count;
			double numberviolation = result[1].getDataDouble();
			double fitvalue = result[2].getDataDouble();
			double mass_new = result[3].getDataDouble();
			System.out.println("\n The [" + the_count + "] generation ");
			System.out.println("Number of violation constrains = " + (int)result[1].getDataDouble());
			System.out.println("Fitness value = " + result[2].getDataDouble());
			System.out.println("Mass value = " + result[3].getDataDouble());
			
			
			/* Store to array for compare */
			try{
				store[i][0] = i;
		    	store[i][1] = numberviolation;
		    	store[i][2] = mass_new;
		    	store[i][3] = fitvalue;	 
			}
			catch (IndexOutOfBoundsException e) {
				System.out.println("Index out of boundary! Check use in store array length");
				return;
			}
			catch (NullPointerException e) {
				System.out.println("Store array is null pointer! Init before use");
				return;
			}

	    	/* 
	    	 * Print out to result.dat file 
	    	 * Format: Generation ith + number of violation constrains + mass value + fitness value 
	    	 * 
	    	 */
	    	
	    	String textwrite = (int)store[i][0] + "," + (int)store[i][1] + "," + store[i][2] + "," + store[i][3] + "\n";
	    	//String filename = "../Sourcecode_Week6/src/result.dat";
	    	mwutils.printResult(filename,textwrite);   	
		}
		
	    /* Find the best generation based on max fitness value and min mass value */
		
    	double best_fitness = store[0][3]; //Start value
    	double best_mass = store[0][2]; //Start value
    	int found = 0;
    	for(int j = 0; j < store.length; j++){
    		if(store[j][3] > best_fitness){		
    			best_fitness = store[j][3];    			
    			best_mass = store[j][2];
    			found = j;
    		}
    	}
    	
    	System.out.println("Best values found at [" + found + "]" + " best fitness = "
    			+ best_fitness + " best mass = " + best_mass );
	}	
	

	@Override
	public void processResult() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void prepareWork() {
		// TODO Auto-generated method stub
		
	}

}
